﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.


using System;
using System.Collections.Generic;
using MS.Internal.Ink;

namespace WpfInk.PresentationCore.System.Windows.Ink;

/// <summary>
/// A helper struct that represents a fragment of a stroke spine.
/// </summary>
internal struct StrokeIntersection
{
    #region Private statics
    private static StrokeIntersection s_empty = new StrokeIntersection(AfterLast, AfterLast, BeforeFirst, BeforeFirst);
    private static StrokeIntersection s_full = new StrokeIntersection(BeforeFirst, BeforeFirst, AfterLast, AfterLast);
    #endregion

    #region Public API

    /// <summary>
    /// BeforeFirst
    /// </summary>
    /// <value></value>
    internal static double BeforeFirst { get { return StrokeFIndices.BeforeFirst; } }

    /// <summary>
    /// AfterLast
    /// </summary>
    /// <value></value>
    internal static double AfterLast { get { return StrokeFIndices.AfterLast; } }

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="hitBegin"></param>
    /// <param name="inBegin"></param>
    /// <param name="inEnd"></param>
    /// <param name="hitEnd"></param>
    internal StrokeIntersection(double hitBegin, double inBegin, double inEnd, double hitEnd)
    {
        //ISSUE-2004/12/06-XiaoTu: should we validate the input?
        _hitSegment = new StrokeFIndices(hitBegin, hitEnd);
        _inSegment = new StrokeFIndices(inBegin, inEnd);
    }

    /// <summary>
    /// hitBeginFIndex
    /// </summary>
    /// <value></value>
    internal double HitBegin
    {
        set { _hitSegment.BeginFIndex = value; }
    }

    /// <summary>
    /// hitEndFIndex
    /// </summary>
    /// <value></value>
    internal double HitEnd
    {
        get { return _hitSegment.EndFIndex; }
        set { _hitSegment.EndFIndex = value; }
    }


    /// <summary>
    /// InBegin
    /// </summary>
    /// <value></value>
    internal double InBegin
    {
        get { return _inSegment.BeginFIndex; }
        set { _inSegment.BeginFIndex = value; }
    }

    /// <summary>
    /// InEnd
    /// </summary>
    /// <value></value>
    internal double InEnd
    {
        get { return _inSegment.EndFIndex; }
        set { _inSegment.EndFIndex = value; }
    }

    /// <summary>
    /// ToString
    /// </summary>
    public override string ToString()
    {
        return "{"  + StrokeFIndices.GetStringRepresentation(_hitSegment.BeginFIndex) + ","
               + StrokeFIndices.GetStringRepresentation(_inSegment.BeginFIndex)  + ","
               + StrokeFIndices.GetStringRepresentation(_inSegment.EndFIndex)    + ","
               + StrokeFIndices.GetStringRepresentation(_hitSegment.EndFIndex)   + "}";
    }


    /// <summary>
    /// Equals
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public override bool Equals(Object obj)
    {
        // Check for null and compare run-time types
        if (obj == null || GetType() != obj.GetType())
            return false;
        return ((StrokeIntersection)obj == this);
    }

    /// <summary>
    /// GetHashCode
    /// </summary>
    /// <returns></returns>
    public override int GetHashCode()
    {
        return _hitSegment.GetHashCode() ^ _inSegment.GetHashCode();
    }


    /// <summary>
    /// operator ==
    /// </summary>
    /// <param name="left"></param>
    /// <param name="right"></param>
    /// <returns></returns>
    public static bool operator ==(StrokeIntersection left, StrokeIntersection right)
    {
        return (left._hitSegment == right._hitSegment && left._inSegment == right._inSegment);
    }

    /// <summary>
    /// operator !=
    /// </summary>
    /// <param name="left"></param>
    /// <param name="right"></param>
    /// <returns></returns>
    public static bool operator !=(StrokeIntersection left, StrokeIntersection right)
    {
        return !(left == right);
    }

    #endregion

    #region Internal API

    /// <summary>
    ///
    /// </summary>
    internal static StrokeIntersection Full { get { return s_full; } }

    /// <summary>
    ///
    /// </summary>
    internal bool IsEmpty { get { return _hitSegment.IsEmpty; } }


    /// <summary>
    ///
    /// </summary>
    internal StrokeFIndices HitSegment
    {
        get { return _hitSegment; }
    }

    /// <summary>
    ///
    /// </summary>
    internal StrokeFIndices InSegment
    {
        get { return _inSegment; }
    }

    #endregion

    #region Internal static methods

    /// <summary>
    /// Get the "in-segments" of the intersections.
    /// </summary>
    internal static StrokeFIndices[] GetInSegments(StrokeIntersection[] intersections)
    {
        global::System.Diagnostics.Debug.Assert(intersections != null);
        global::System.Diagnostics.Debug.Assert(intersections.Length > 0);

        List<StrokeFIndices> inFIndices = new List<StrokeFIndices>(intersections.Length);
        for (int j = 0; j < intersections.Length; j++)
        {
            global::System.Diagnostics.Debug.Assert(!intersections[j].IsEmpty);
            if (!intersections[j].InSegment.IsEmpty)
            {
                if (inFIndices.Count > 0 &&
                    inFIndices[inFIndices.Count - 1].EndFIndex >=
                    intersections[j].InSegment.BeginFIndex)
                {
                    //merge
                    StrokeFIndices sfiPrevious = inFIndices[inFIndices.Count - 1];
                    sfiPrevious.EndFIndex = intersections[j].InSegment.EndFIndex;
                    inFIndices[inFIndices.Count - 1] = sfiPrevious;
                }
                else
                {
                    inFIndices.Add(intersections[j].InSegment);
                }
            }
        }
        return inFIndices.ToArray();
    }

    /// <summary>
    /// Get the "hit-segments"
    /// </summary>
    internal static StrokeFIndices[] GetHitSegments(StrokeIntersection[] intersections)
    {
        global::System.Diagnostics.Debug.Assert(intersections != null);
        global::System.Diagnostics.Debug.Assert(intersections.Length > 0);

        List<StrokeFIndices> hitFIndices = new List<StrokeFIndices>(intersections.Length);
        for (int j = 0; j < intersections.Length; j++)
        {
            global::System.Diagnostics.Debug.Assert(!intersections[j].IsEmpty);
            if (!intersections[j].HitSegment.IsEmpty)
            {
                if (hitFIndices.Count > 0 &&
                    hitFIndices[hitFIndices.Count - 1].EndFIndex >=
                    intersections[j].HitSegment.BeginFIndex)
                {
                    //merge
                    StrokeFIndices sfiPrevious = hitFIndices[hitFIndices.Count - 1];
                    sfiPrevious.EndFIndex = intersections[j].HitSegment.EndFIndex;
                    hitFIndices[hitFIndices.Count - 1] = sfiPrevious;
                }
                else
                {
                    hitFIndices.Add(intersections[j].HitSegment);
                }
            }
        }
        return hitFIndices.ToArray();
    }

    #endregion

    #region  Fields

    private StrokeFIndices _hitSegment;
    private StrokeFIndices _inSegment;

    #endregion
}